package com.cdk8s.example.simplespringboot.utils;

import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import com.google.common.base.CaseFormat;
import com.google.common.base.Splitter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

/**
 * 字符串工具类, 继承StringUtils类.
 */
@Slf4j
public final class StringUtil {

	public static final String EMPTY = "";

	// =====================================Apache Common 包 start=====================================

	/**
	 * eg：
	 * MessageFormat.format("name={1}, age={0}, {1}", 25, "huhx");
	 * name=huhx, age=25, huhx
	 */
	public static String format(String pattern, Object... arguments) {
		return MessageFormat.format(pattern, arguments);
	}

	public static boolean isNotBlank(final String str) {
		return StringUtils.isNotBlank(str);
	}

	public static boolean isBlank(final String str) {
		return StringUtils.isBlank(str);
	}

	public static boolean containsAny(final String str, final CharSequence... search) {
		return StringUtils.containsAny(str, search);
	}

	public static boolean containsIgnoreCase(final String str, final String search) {
		return StringUtils.containsIgnoreCase(str, search);
	}

	/**
	 * 获取域名的 host 不包含 protocol
	 *
	 * @param str
	 * @return
	 */
	public static String getDomainHostNotIncludeProtocol(final String str) {
		if (isBlank(str)) {
			return null;
		}

		URL url = URLUtil.toUrlForHttp(str);
		// String protocol = url.getProtocol() + "://";
		return url.getHost();
	}

	/**
	 * 由于一些国内 cn 域名的特殊性的，可能还需要继续完善
	 *
	 * @param str
	 * @return
	 */
	public static String getDomainTopHostNotIncludeProtocol(final String str) {
		if (isBlank(str)) {
			return null;
		}
		List<String> specialList = new ArrayList<>();
		specialList.add("edu.cn");
		specialList.add("com.cn");
		specialList.add("org.cn");
		specialList.add("net.cn");

		URL url = URLUtil.toUrlForHttp(str);
		// String protocol = url.getProtocol() + "://";
		String host = url.getHost();
		List<String> split = split(host, ".");


		if (CollectionUtil.isNotEmpty(split) && split.size() >= 2) {
			String result = split.get(split.size() - 2) + "." + split.get(split.size() - 1);
			if (specialList.contains(result)) {
				if (split.size() >= 3) {
					return split.get(split.size() - 3) + "." + split.get(split.size() - 2) + "." + split.get(split.size() - 1);
				}
			}
			return result;
		}

		return null;
	}

	/**
	 * 字符串相似度判断，返回格式：0.7647058824
	 *
	 * @param str1
	 * @param str2
	 * @return
	 */
	public static double similar(final String str1, final String str2) {
		return StrUtil.similar(str1, str2);
	}

	/**
	 * 返回字符串第一次出现的下标位置，如果不存在返回 -1。如果存返回从 0 开始的下标。
	 */
	public static int indexOf(final String str, final String search) {
		return StringUtils.indexOf(str, search);
	}

	/**
	 * 根据下标截取字符串
	 */
	public static String substring(final String str, final int start, final int end) {
		return StringUtils.substring(str, start, end);
	}

	/**
	 * 根据前后字符串截取字符
	 */
	public static String substringBetween(final String str, final String open, final String close) {
		return StringUtils.substringBetween(str, open, close);
	}

	/**
	 * 截取第一个 search 匹配到之后的字符串，不包含 search 字符
	 * 比如：(abbccddef,c) 得到的结果就是：cddef
	 */
	public static String substringAfter(final String str, final String search) {
		return StringUtils.substringAfter(str, search);
	}

	/**
	 * 截取第一个 search 匹配到之前的字符串，不包含 search 字符
	 * 比如：(abbccddef,c) 得到的结果就是：abb
	 */
	public static String substringBefore(final String str, final String search) {
		return StringUtils.substringBefore(str, search);
	}

	/**
	 * 截取最后一个 search 匹配到之前的字符串，不包含 search 字符
	 * 比如：(abbccddef,c) 得到的结果就是：abbc
	 */
	public static String substringBeforeLast(final String str, final String search) {
		return StringUtils.substringBeforeLast(str, search);
	}

	/**
	 * 截取最后一个 search 匹配到之后的字符串，不包含 search 字符
	 * 比如：(abbccddef,c) 得到的结果就是：ddef
	 */
	public static String substringAfterLast(final String str, final String search) {
		return StringUtils.substringAfterLast(str, search);
	}

	/**
	 * 删除前后空格
	 */
	public static String trim(final String str) {
		return StringUtils.trim(str);
	}

	/**
	 * 删除前后空格，如果为空字符串则返回 null
	 */
	public static String trimToNull(final String str) {
		return StringUtils.trimToNull(str);
	}

	/**
	 * 拼接字符串
	 * StringUtils.join(null)            = null
	 * StringUtils.join([])              = ""
	 * StringUtils.join([null])          = ""
	 * StringUtils.join(["a", "b", "c"]) = "abc"
	 * StringUtils.join([null, "", "a"]) = "a"
	 */
	public static String join(final String... elements) {
		return StringUtils.join(elements);
	}

	/**
	 * 拼接字符串
	 * StringUtils.joinWith(",", {"a", "b"})        = "a,b"
	 * StringUtils.joinWith(",", {"a", "b",""})     = "a,b,"
	 * StringUtils.joinWith(",", {"a", null, "b"})  = "a,,b"
	 * StringUtils.joinWith(null, {"a", "b"})       = "ab"
	 */
	public static String joinWith(final String delimiter, final String... elements) {
		return StringUtils.joinWith(delimiter, elements);
	}

	public static boolean equals(final String str1, final String str2) {
		return StringUtils.equals(str1, str2);
	}

	public static boolean notEquals(final String str1, final String str2) {
		return !StringUtils.equals(str1, str2);
	}

	public static boolean equalsIgnoreCase(final String str1, final String str2) {
		return StringUtils.equalsIgnoreCase(str1, str2);
	}

	public static boolean notEqualsIgnoreCase(final String str1, final String str2) {
		return !StringUtils.equalsIgnoreCase(str1, str2);
	}

	public static String lowerCase(final String str1) {
		return StringUtils.lowerCase(str1);
	}

	public static String upperCase(final String str1) {
		return StringUtils.upperCase(str1);
	}

	/**
	 * 检查字符串是否以某个字符串开头
	 */
	public static boolean startsWith(final String str, final String prefix) {
		return StringUtils.startsWith(str, prefix);
	}

	/**
	 * 检查字符串是否以某几个中的任意一个字符串开头
	 */
	public static boolean startsWithAny(final String str, final List<String> stringList) {
		String[] strings = CollectionUtil.toArray(stringList, String.class);
		return StringUtils.startsWithAny(str, strings);
	}

	/**
	 * 检查字符串是否以某个字符串结尾
	 */
	public static boolean endsWith(final String str, final String suffix) {
		return StringUtils.endsWith(str, suffix);
	}

	public static boolean endsWithIgnoreCase(final String str, final String suffix) {
		return StringUtils.endsWithIgnoreCase(str, suffix);
	}

	/**
	 * 检查字符串是否以某几个中的任意一个字符串结尾
	 */
	public static boolean endsWithAny(final String str, final List<String> stringList) {
		String[] strings = CollectionUtil.toArray(stringList, String.class);
		return StringUtils.endsWithAny(str, strings);
	}

	public static boolean endsWithAnyIgnoreCase(final String str, final List<String> stringList) {
		for (String temp : stringList) {
			if (equalsIgnoreCase(str, temp)) {
				return true;
			}
		}
		return false;
	}

	public static boolean endsWithAny(final CharSequence sequence, final CharSequence... searchStrings) {
		return StringUtils.endsWithAny(sequence, searchStrings);
	}

	/**
	 * 如果有多个位置被匹配到，也只有第一个匹配会被替换
	 */
	public static String replaceOnce(final String text, final String searchString, final String replacement) {
		return StringUtils.replaceOnce(text, searchString, replacement);
	}

	/**
	 * 如果有多个位置被匹配到，都会被替换
	 */
	public static String replace(final String text, final String searchString, final String replacement) {
		return StringUtils.replace(text, searchString, replacement);
	}

	/**
	 * 如果有多个位置被匹配到，则根据 max 次数替换。如果 max = 2，则只表示匹配两次替换即可
	 */
	public static String replace(final String text, final String searchString, final String replacement, final int max) {
		return StringUtils.replace(text, searchString, replacement, max);
	}

	public static String replaceIgnoreCase(final String text, final String searchString, final String replacement) {
		return StringUtils.replaceIgnoreCase(text, searchString, replacement);
	}

	/**
	 * 注意：被删除的部分必须是在最尾巴，不然不会被删除，原值返回
	 */
	public static String removeEnd(final String text, final String searchString) {
		return StringUtils.removeEnd(text, searchString);
	}

	/**
	 * 注意：被删除的部分必须是在最前面，不然不会被删除，原值返回
	 */
	public static String removeStart(final String text, final String searchString) {
		return StringUtils.removeStart(text, searchString);
	}

	public static String remove(final String text, final String searchString) {
		return StringUtils.remove(text, searchString);
	}

	public static String removeByList(String text, final List<String> searchStringList) {
		if (CollectionUtil.isEmpty(searchStringList)) {
			return text;
		}
		for (String searchString : searchStringList) {
			text = StringUtils.remove(text, searchString);
		}
		return text;
	}

	/**
	 * 比如：(aa#bb#, #) = aa,bb（一共 2 个元素）
	 */
	public static List<String> split(String str, String separator) {
		return CollectionUtil.toList(StringUtils.split(str, separator));
	}

	public static List<String> splitAndTrim(String str, String separator) {
		List<String> result = new ArrayList<>();
		List<String> split = split(str, separator);
		for (String temp : split) {
			result.add(trim(temp));
		}
		return result;
	}

	/**
	 * 比如：(aa#bb#, #) = aa,bb,""（一共 3 个元素）
	 */
	public static List<String> splitByWholeSeparator(String str, String separator) {
		return CollectionUtil.toList(StringUtils.splitByWholeSeparator(str, separator));
	}

	/**
	 * 比如：(":cd:ef:", ":") = ["", cd", "ef", ""]，保留所有元素，包括空字符
	 */
	public static List<String> splitPreserveAllTokens(String str, String separator) {
		return CollectionUtil.toList(StringUtils.splitPreserveAllTokens(str, separator));
	}

	/**
	 * 多个符号都可以进行分割(Guava)
	 * 比如：("apple.banana,,orange,,.", "[.|,]") = [apple, banana, orange]
	 */
	public static List<String> splitOnPatternByGuava(String str, String separatorPattern) {
		return Splitter.onPattern(separatorPattern).omitEmptyStrings().splitToList(str);
	}

	/**
	 * 相反设置
	 */
	public static String uncapitalize(String str) {
		return StringUtils.uncapitalize(str);
	}

	// =====================================Apache Common 包 end=====================================

	// =====================================其他包 start=====================================

	/**
	 * 大写开头的驼峰改为小写开头的驼峰
	 */
	public static String upperCamelToLowerCamel(String str) {
		CaseFormat fromFormat = CaseFormat.UPPER_CAMEL;
		CaseFormat toFormat = CaseFormat.LOWER_CAMEL;
		return fromFormat.to(toFormat, str);
	}

	/**
	 * 小写开头的驼峰改为大写开头的驼峰
	 */
	public static String lowerCamelToUpperCamel(String str) {
		CaseFormat fromFormat = CaseFormat.LOWER_CAMEL;
		CaseFormat toFormat = CaseFormat.UPPER_CAMEL;
		return fromFormat.to(toFormat, str);
	}

	/**
	 * 下划线的字符串转换为驼峰式字符串
	 */
	public static String lowerUnderscoreToLowerCamel(String str) {
		CaseFormat fromFormat = CaseFormat.LOWER_UNDERSCORE;//
		CaseFormat toFormat = CaseFormat.LOWER_CAMEL;
		return fromFormat.to(toFormat, str);
	}

	/**
	 * 驼峰式的字符串转换为下划线
	 */
	public static String lowerCamelToLowerUnderscore(String str) {
		CaseFormat fromFormat = CaseFormat.LOWER_CAMEL;
		CaseFormat toFormat = CaseFormat.LOWER_UNDERSCORE;
		return fromFormat.to(toFormat, str);
	}

	/**
	 * 去除 html 标签
	 */
	public static String removeHtml(String str) {
		if (isBlank(str)) {
			return "";
		}
		return str.replaceAll("\\&[a-zA-Z]{1,10};", "").replaceAll("<[^>]*>", "").replaceAll("[(/>)<]", "");
	}

	/**
	 * 验证value是否包含Xss 包含返回false不包含返回true
	 */
	public static boolean checkXss(String value) {

		if (isBlank(value)) {
			return true;
		}

		// NOTE: It's highly recommended to use the ESAPI library and uncomment
		// the following line to
		// avoid encoded attacks.
		// value = ESAPI.encoder().canonicalize(value);
		// Avoid null characters
		// Avoid anything between script tags
		Pattern scriptPattern = Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE);
		if (scriptPattern.matcher(value).find()) {
			log.error("------zch------xss contain script");
			return false;
		}

		// Remove any lonesome </script> tag
		scriptPattern = Pattern.compile("</script>", Pattern.CASE_INSENSITIVE);
		if (scriptPattern.matcher(value).find()) {
			log.error("------zch------xss contain script");
			return false;
		}

		// Remove any lonesome <script ...> tag
		scriptPattern = Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
		if (scriptPattern.matcher(value).find()) {
			log.error("------zch------xss contain script");
			return false;
		}

		scriptPattern = Pattern.compile("<iframe(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
		if (scriptPattern.matcher(value).find()) {
			log.error("------zch------xss contain iframe");
			return false;
		}

		scriptPattern = Pattern.compile("<form(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
		if (scriptPattern.matcher(value).find()) {
			log.error("------zch------xss contain form");
			return false;
		}

		scriptPattern = Pattern.compile("<base(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
		if (scriptPattern.matcher(value).find()) {
			log.error("------zch------xss contain base");
			return false;
		}

		// Avoid eval(...) e­xpressions
		scriptPattern = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
		if (scriptPattern.matcher(value).find()) {
			log.error("------zch------xss contain eval");
			return false;
		}

		// Avoid e­xpression(...) e­xpressions
		scriptPattern = Pattern.compile("e­xpression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
		if (scriptPattern.matcher(value).find()) {
			log.error("------zch------xss contain e­xpression");
			return false;
		}

		// Avoid javascript:... e­xpressions
		scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE);
		if (scriptPattern.matcher(value).find()) {
			log.error("------zch------xss contain javascript");
			return false;
		}

		// Avoid vbscript:... e­xpressions
		scriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE);
		if (scriptPattern.matcher(value).find()) {
			log.error("------zch------xss contain vbscript");
			return false;
		}

		// Avoid onload= e­xpressions
		scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
		if (scriptPattern.matcher(value).find()) {
			log.error("------zch------xss contain onload");
			return false;
		}

		// 单引号
		scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
		if (scriptPattern.matcher(value).find()) {
			if (StringUtil.containsIgnoreCase(value, ".js")) {
				log.error("------zch------xss contain src and .js");
				// img 标签必然会有 src，所以这里只能简单判断是否含有 .js 文件
				return false;
			}
			// return false;
		}

		// 双引号
		scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
		if (scriptPattern.matcher(value).find()) {
			if (StringUtil.containsIgnoreCase(value, ".js")) {
				log.error("------zch------xss contain src and .js");
				// img 标签必然会有 src，所以这里只能简单判断是否含有 .js 文件
				return false;
			}
			// return false;
		}

		value = value.replaceAll("%(?![0-9a-fA-F]{2})", "%25");
		value = value.replaceAll("\\+", "%2B");
		String valueByURLDecoder = null;
		try {
			valueByURLDecoder = URLDecoder.decode(value, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			return true;
		}

		scriptPattern = Pattern.compile("</script>", Pattern.CASE_INSENSITIVE);
		if (scriptPattern.matcher(valueByURLDecoder).find()) {
			log.error("------zch------xss contain script");
			return false;
		}

		scriptPattern = Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
		if (scriptPattern.matcher(valueByURLDecoder).find()) {
			log.error("------zch------xss contain script");
			return false;
		}

		scriptPattern = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
		if (scriptPattern.matcher(valueByURLDecoder).find()) {
			log.error("------zch------xss contain eval");
			return false;
		}

		scriptPattern = Pattern.compile("e­xpression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
		if (scriptPattern.matcher(valueByURLDecoder).find()) {
			log.error("------zch------xss contain e­xpression");
			return false;
		}

		scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE);
		if (scriptPattern.matcher(valueByURLDecoder).find()) {
			log.error("------zch------xss contain javascript");
			return false;
		}

		scriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE);
		if (scriptPattern.matcher(valueByURLDecoder).find()) {
			log.error("------zch------xss contain vbscript");
			return false;
		}

		scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
		if (scriptPattern.matcher(valueByURLDecoder).find()) {
			log.error("------zch------xss contain onload");
			return false;
		}

		scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
		if (scriptPattern.matcher(valueByURLDecoder).find()) {
			if (StringUtil.containsIgnoreCase(value, ".js")) {
				log.error("------zch------xss contain src and .js");
				// img 标签必然会有 src，所以这里只能简单判断是否含有 .js 文件
				return false;
			}
			// return false;
		}

		scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
		if (scriptPattern.matcher(valueByURLDecoder).find()) {
			if (StringUtil.containsIgnoreCase(value, ".js")) {
				log.error("------zch------xss contain src and .js");
				// img 标签必然会有 src，所以这里只能简单判断是否含有 .js 文件
				return false;
			}
			// return false;
		}

		return true;
	}

	/**
	 * 删除两个字符串之间的内容，包含查询字符串本身内容
	 */
	public static String removeRangeString(String body, String str1, String str2) {
		while (true) {
			int index1 = body.indexOf(str1);
			if (index1 != -1) {
				int index2 = body.indexOf(str2, index1);
				if (index2 != -1) {
					String str3 = body.substring(0, index1) + body.substring(index2 + str2.length());
					body = str3;
				} else {
					return body;
				}
			} else {
				return body;
			}
		}
	}

	/**
	 * 根据分隔符拆分字符串，取各自单词的首字母组成小写的词
	 * 比如：sys_user == su
	 */
	public static String lowerFirstFromSeparator(String str, String separator) {
		if (isNotBlank(str) && isNotBlank(separator)) {
			List<String> split = split(str, separator);
			StringBuilder stringBuilder = new StringBuilder();

			for (String temp : split) {
				stringBuilder.append(Character.toLowerCase(temp.charAt(0)));
			}

			return stringBuilder.toString();
		}
		return null;
	}


	/**
	 * 对字符串处理:将指定位置到指定位置的字符以星号代替
	 *
	 * @param content 传入的字符串
	 * @param begin   开始位置
	 * @param end     结束位置
	 * @return
	 */
	public static String getStarString(String content, int begin, int end) {

		if (begin >= content.length() || begin < 0) {
			return content;
		}
		if (end >= content.length() || end < 0) {
			return content;
		}
		if (begin >= end) {
			return content;
		}
		String starStr = "";
		for (int i = begin; i < end; i++) {
			starStr = starStr + "*";
		}
		return content.substring(0, begin) + starStr + content.substring(end, content.length());
	}


	/**
	 * 对字符加星号处理：除前面几位和后面几位外，其他的字符以星号代替
	 *
	 * @param content  传入的字符串
	 * @param frontNum 保留前面字符的位数
	 * @param endNum   保留后面字符的位数
	 * @return 带星号的字符串
	 */

	public static String getStarString2(String content, int frontNum, int endNum) {

		if (frontNum >= content.length() || frontNum < 0) {
			return content;
		}
		if (endNum >= content.length() || endNum < 0) {
			return content;
		}
		if (frontNum + endNum >= content.length()) {
			return content;
		}
		String starStr = "";
		for (int i = 0; i < (content.length() - frontNum - endNum); i++) {
			starStr = starStr + "*";
		}
		return content.substring(0, frontNum) + starStr
				+ content.substring(content.length() - endNum, content.length());

	}

	// =====================================其他包 end=====================================


	// =====================================私有方法 start=====================================

	// =====================================私有方法 end=====================================

}



